# Copyright (c) 2005,2006,2007 Dynamic Solutions Inc. (support@dynamic-solutions.com)
#
# This file is part of DataZoomer.
#
# DataZoomer is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# DataZoomer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""Generate HTML"""


# Create an empty dictionary that is used for substitution in hReplaceTags
import sys
from dzcontrol import Control, Tagged
from dzutil import url_for, link_to
from dzmodel import RecordIterator

dic = {}


# Classes added by SH - 2007

class HtmlTag(Control):
    def __init__(self,thetype,**attributes):
        Control.__init__(self)
        self.thetype = thetype
        self.attributes = attributes
        self._dlist=[]
        self.inline=0

    def _override(self,k,v):
        """override function for the img attributes"""
        if k.lower()=='_class': return ('class',v)
        else: return (k,v)

    def accept(self,visitor,result):
        if not self.inline or len(self._dlist)>0: self.visit_htmltag(result)
        else: self.visit_htmltag_inline(result)

    def visit_htmltag(self,result):
        result.append('')
        attr = ['%s="%s"' % self._override(k,v) for k,v in self.attributes.items()]
        result.append('<%s%s>' % (self.thetype,(attr and ' %s' % ' '.join(attr) or '')))
        result.append(' '.join(self._dlist))
        result.append('</%s>' % self.thetype)

    def visit_htmltag_inline(self,result):
        result.append('')
        attr = ['%s="%s"' % self._override(k,v) for k,v in self.attributes.items()]
        result.append('<%s%s />' % (self.thetype,(attr and ' %s' % ' '.join(attr) or '')))

    def add(self,html):
        self._dlist.append(str(html))

class HtmlDiv(Control):
    def __init__(self,**attributes):
        Control.__init__(self)
        self.attributes = attributes
        self._dlist=[]

    def _override(self,k,v):
        """override function for the img attributes"""
        if k.lower()=='_class': return ('class',v)
        else: return (k,v)

    def accept(self,visitor,result):
        self.visit_div(result)

    def visit_div(self,result):
        result.append('')
        attr = ['%s="%s"' % self._override(k,v) for k,v in self.attributes.items()]
        result.append('<div%s>' % ((attr and ' %s' % ' '.join(attr) or '')))
        result.append(' '.join(self._dlist))
        result.append('</div>')

    def add(self,html):
        self._dlist.append(str(html))

class HtmlList(Control):
    def __init__(self,**attributes):
        Control.__init__(self)
        self.attributes = attributes
        self._tlist=[]

    def _override(self,k,v):
        """override function for the img attributes"""
        if k.lower()=='_class': return ('class',v)
        else: return (k,v)

    def accept(self,visitor,result):
        self.visit_htmllist(result)

    def visit_htmllist(self,result):
        result.append('')
        attr = ['%s="%s"' % self._override(k,v) for k,v in self.attributes.items()]
        result.append('<ul%s>' % ((attr and ' %s' % ' '.join(attr) or '')))
        result.append(self.get_list())
        result.append('</ul>')

    def add(self,txt):
        self._tlist.append(txt)

    def get_list_item(self,txt):
        return '<li>%s</li>' % txt

    def get_list(self):
        return ''.join(self.get_list_item(t) for t in self._tlist)

class HtmlImage(Control):
    def __init__(self,elementid=None,**attributes):
        Control.__init__(self)
        self.id = elementid
        self.attributes = attributes
        if 'class' not in self.attributes.keys() and '_class' not in self.attributes.keys():
            self.attributes['class'] = 'icon'

    def accept(self,visitor,result):
        self.visit_image(result)

    def _override(self,k,v):
        """override function for the img attributes"""
        if k.lower()=='src' and v.find('/')==-1: return (k,"<#IMG>%s" % v)
        elif k.lower()=='_class': return ('class',v)
        else: return (k,v)

    def visit_image(self,result):
        """The view of the image"""
        attr = ['%s="%s"' % self._override(k,v) for k,v in self.attributes.items()]
        id = (self.id and ' id="%s"' % self.id or '')
        result.append('<img%s%s />' % (id,(attr and ' %s' % ' '.join(attr) or '')))

class HtmlTable(Control):
    default_table_class = 'baselist'
    default_zebra_row1 = 'zrow1'
    default_zebra_row2 = 'zrow2'

    def __init__(self,dataset=None, title='List of Sessions', **attributes):
        """HtmlTable initialization"""
        Control.__init__(self)
        self.table_class = 'baselist'
        self.zebra_row1 = 'zrow1'
        self.zebra_row2 = 'zrow2'
        self.zebra = 1

        self.dataset    = dataset
        if not 'indexname' in self.dataset.__dict__: self.dataset.indexname = ''
        self.title      = title
        self.attributes = self.__default_attributes__(attributes)
        self.sortdir    = self.sort_direction()

        self.table_actions = {'sort':{'action':'sort'},'click':{'action':'show'}}
        self.actions = {'trash': {'title':'Delete','icons':{},'attributes':{}}}
        self.action_heading = None
        self.action_type = 'icon'
        self.row_attributes = {'class':self.zebra_row1}
        self.header_attributes = {'class':'baseheader'}
        self.column_attributes = {} ## could this be on the column obj

        self.fields     = []
        self.labels     = {}

        ## compound indexes
        self.key_fields = ('ID',)

    def __default_attributes__(self,attr):
        """default table attributes that will be used
            only set the class as the default is to use css, but you can
            send attributes in when desired
        """
        attr.setdefault('class',attr.get('_class',self.table_class))
        if '_class' in attr.keys(): del attr['_class']
        return attr

    def on_action(self,action):
        """return the action/event handler name for the given 'action'"""
        return self.table_actions.get(action,None)

    def on_click(self):
        """return the on_click action/event handler"""
        return self.on_action('click')

    def on_sort(self):
        """return the on_sort action/event handler"""
        return self.on_action('sort')

    def sort_direction(self,direction='d'):
        """Sort direction indicator to be used with column sort links"""
        return (direction=='a' and 'd' or 'a')

    def set_direction(self,direction):
        """set the sort direction coming back from the control"""
        self.sortdir = direction

    def sort_url(self,col):
        """return a sort url"""
        return '<a href="%s" class="forumlink">%s</a>' % (
            url_for(action=self.on_sort()),self.column_name(col))

    def column_name(self,col):
        """Maps a column name to a display name.  If there is no display name set
            then the column name is used"""
        return self.labels.get(col,col.capitalize())

    def column_label(self,col):
        """Return the column name or link"""
        return ( 'sort' in self.table_actions.keys() and self.sort_url(col) or self.column_name(col) )

    def accept(self,visitor,result):
        """The controls accept method"""
        self.visit_htmltable(result)

    def get_row_attributes(self):
        """table row attributes function, used with no zebra stripe on rows"""
        return ['%s="%s"' % (k,v) for k,v in self.row_attributes.items()]

    def get_zebra_row_attributes(self):
        """table row attributes function, used with zebra stripes on rows"""
        self.row_attributes['class'] = (self.row_attributes['class']==self.zebra_row2 and self.zebra_row1 or self.zebra_row2)
        return ['%s="%s"' % (k,v) for k,v in self.row_attributes.items()]

    def cell_data(self,row,col):
        """return the cell data for the column.  Format the drill-down link as specified"""
        data = row.__getattr__(col.name)
        click_action = 'click' in self.table_actions.keys()
        # is there a field specified that we want to use to drill-down on (other than the table index field)
        drill_down_field = (click_action and self.table_actions['click'].get('field',None) or None)
        # if (dataset has an index) and (we want a drill-down action) and (this is the drill-down field)
        if self.dataset.indexname and click_action and (
            col.name==drill_down_field or
            ( drill_down_field==None and col.name==self.dataset.indexname) ):
            key = row.__getattr__(self.dataset.indexname)
            text = self.table_actions['click'].get('title',None)
            # use the column data information if title references a dataset column name
            text = (not text in self.dataset.rs.columns.names() and text or row.__getattr__(text))
            # otherwise default to a standard title
            text = (text and text or '%s %s Detail' % (self.dataset.indexname,key))
            data = link_to( (row.__getattr__(drill_down_field) or str(key)),
                            title=text,
                            action=self.table_actions['click']['action'],
                            key=key )
        return (data and data or '&nbsp;')

    def visit_htmltable(self,result):
        """The controls look/view"""
        columns = (isinstance(self.dataset,RecordIterator) and self.dataset.rs.columns or self.dataset.columns)
        if self.fields:
            ## this could be better
            column_order = []
            for fieldname in self.fields:
                for column in columns:
                    if column.name==fieldname: column_order.append(column)
            columns = column_order

        # <TABLE>: The table tag and specified attributes
        attr = ['%s="%s"' % (k,v) for k,v in self.attributes.items()]
        result.append('<table%s>' % ((attr and ' %s' % ' '.join(attr) or '')))

        # <TH>: Table Headings
        result.append('<tr>')
        col_attr = ['%s="%s"' % (k,v) for k,v in self.header_attributes.items()]
        for column in columns:
            if not self.fields or column.name in self.fields:
                result.append('<th%s>%s</th>' % (
                            (col_attr and ' %s' % ' '.join(col_attr) or ''),
                            self.column_label(column.name)
                          ))

        # Add padding cells for any row level action links
        #  if we know the index for the table
        if self.dataset.indexname:
            if self.action_heading and self.actions:
                result.append('<th%s>%s</th>'% ((col_attr and ' %s' % ' '.join(col_attr) or ''),self.action_heading) )
            else:
                for action in self.actions.keys():
                    result.append('<th>&nbsp;</th>')
        result.append('</tr>')


        # <TR>: Table Rows
        row_attr_fn = (self.zebra==1 and self.get_zebra_row_attributes or self.get_row_attributes)
        for row in self.dataset:
            row_attr = row_attr_fn()
            result.append('<tr%s>' % (row_attr and ' %s' % ' '.join(row_attr) or ''))

            # <TD>: Cells
            for column in columns:
                if not self.fields or column.name in self.fields:
                    cell_attr = ['%s="%s"' % (k,v) for k,v in self.column_attributes.items()]

                    result.append('<td%s>%s</td>' % (
                                (cell_attr and ' %s' % ' '.join(cell_attr) or ''),
                                self.cell_data(row,column)))

            # Add actions
            #  if we know the index for the table
            if self.dataset.indexname:
                cell_mask = ((self.action_heading and self.actions) and '%s' or '<td>%s</td>')
                if self.action_heading and self.actions: result.append('<td>')
                for aname,adict in self.actions.items():
                    if not 'icons' in adict.keys(): adict['icons'] = {}
                    if not 'attributes' in adict.keys(): adict['attributes'] = {}
                    img = HtmlImage( src=adict['icons'].get('src',"%s.png" % aname),
                                        title=adict['icons'].get('title',''),
                                        alt=adict['icons'].get('alt',adict['icons'].get('title','')),
                                        _class=adict['icons'].get('class','icon'),
                                   )

                    action = Action(adict.get('title',aname),adict.get('title',''),action=aname,key=row.__getitem__(self.dataset.indexname),**adict['attributes'])
                    action.set_image(img)
                    fn = 'as_%s' % self.action_type
                    result.append(cell_mask % (fn in action.__class__.__dict__ and eval('action.%s'%fn)() or action.as_icon()))
                if self.action_heading and self.actions: result.append('</td>')

            result.append('</tr>')
        result.append('</table>')
 
 
def HtmlEdit(rec,map_list={},ignore_list=[]):
    """HTML Edit Record for a form"""
    result = []
    result.append('<table>')
    for column in rec._columns_:
        if column.name not in ignore_list:
          display_name = map_list.get(column.name,column.name.capitalize())
          result.append('<tr><td valign=top>')
          if column.type=='TEXT':
              result.append('%s</td><td><textarea name=%s cols=60 rows=4><#%s></textarea></td></tr>' % (display_name,column.name.upper(),column.name))
          elif column.type=='DATE':
              result.append('%s</td><td><input type=text name=%s value="<#%s>" size=%s> (date)</td></tr>' % (display_name,column.name.upper(),column.name,column.size))
          elif column.type=='NUMERIC':
              result.append('%s</td><td><input type=text name=%s value="<#%s>" size=%s> (numeric)</td></tr>' % (display_name,column.name.upper(),column.name,column.size))
          else:
              if column.name=='PASSWORD':
                  result.append('%s</td><td><input type=password name=%s size=%s value="<#%s>" maxlength=%s></td></tr>' % (display_name,column.name.upper()+'1',column.size,column.name,column.size))
                  result.append('<tr><td>%s</td><td><input type=password name=%s size=%s value="<#%s>" maxlength=%s></td></tr>' % ('',column.name.upper()+'2',column.size,column.name,column.size))
              else:
                  result.append('%s</td><td><input type=text name=%s size=%s value="<#%s>" maxlength=%s></td></tr>' % (display_name,column.name.upper(),column.size,column.name,column.size))
    result.append('</table><br>')

    html = ''.join(result)

    tagged = Tagged(html)
    for field in rec.keys():
        tagged.__dict__[field] = rec[field]

    return tagged

class Action:
    def __init__(self,myname,mytitle,**link_attributes):
        self.linkname = myname
        self.titleattr = mytitle
        self.attr = link_attributes
        self.img = None

    def __str__(self):
        """Action defaults to a link format"""
        return self.as_link()

    def __repr__(self):
        """Action defaults to a link format"""
        return self.as_link()

    def as_link(self):
        """Return the action in an html anchor/link format"""
        return link_to(self.linkname,title=self.titleattr,**self.attr)

    def as_url(self):
        """Return the action as url parameters"""
        return url_for(**self.attr)

    def set_image(self,image):
        """Set an image for the action"""
        self.img = image

    def as_icon(self):
        """Return the action in an image/icon url link format"""
        return '<a href="%s">%s</a>' % (self.as_url(),self.img)

    def as_button(self):
        """Not implemented"""
        pass

